fishaudio: reuse streaming websocket and prebuffer initial chunks#2017
Merged
Conversation
🦋 Changeset detectedLatest commit: 19abcfd The changes in this PR will be included in the next version bump. This PR includes changesets to release 36 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Preserve immediate first-frame delivery while reusing prewarmed websockets through the shared connection pool. Co-authored-by: Cursor <cursoragent@cursor.com>
Reject stale prewarm connections and replace sockets that close while idle so model changes and subsequent turns remain correctly configured. Co-authored-by: Cursor <cursoragent@cursor.com>
toubatbrian
approved these changes
Jul 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reconcile the Fish Audio streaming websocket port with the superseding Python and JavaScript behavior now on
main./v1/tts/livewebsocket connections through the existing JSConnectionPooland exposeTTS.prewarm().TTS.close()and remove failed or idle-closed sockets before reuse.modelchanges because Fish Audio sends the model in the websocket handshake header.Source diff coverage
This reconciles both Python sources and current JS
main:JavaScript adaptations:
300_000).wslifecycle andreadyStatechecks replace aiohttp websocket behavior.ConnectionPool.withConnectionhandles checkout, return, abort, and failure removal.ConnectionPoolas public Fish Audio API.TDD and verification
RED lifecycle tests initially reported 3 failures:
s2-proprewarm survived a change tos2.1-pro.GREEN:
plugins/fishaudio/src/tts.test.tspasses all 10 tests, covering sequential reuse, prewarming, completed and in-flight model invalidation, failure and close cleanup, idle closure, immediate first-frame emission, and final-frame handling.Passed checks:
pnpm test -- --run plugins/fishaudio/src/tts.test.ts.pnpm --filter @livekit/agents-plugin-fishaudio lint.pnpm --filter @livekit/agents-plugin-fishaudio build.pnpm build.pnpm lint.pnpm format:check.The Fish Audio changeset is a patch release.
Cue voice E2E
sid_f5a682549ea6.19abcfd199b0cd58144aa8df5ec0fd130a951086.Turn 1 waited for full playout with:
It resolved at session-relative 9513 ms. TTS TTFB was 156.669 ms. Prewarm opened upgrade #1 with model
s2-pro; the first turn reused that same connection, with no second upgrade before synthesis.Turn 2 changed the model and waited for the tool, speech, and full playout:
It resolved at session-relative 24214 ms. TTS TTFB was 147.254 ms. Exactly upgrade #2 opened with model
s2.1-pro, followed by successful speech and playout.Immediate-frame evidence:
stream.next()from the first complete provider audio event before a second provider event is sent.playout completed without interruptionfor both turns.Persisted artifacts and SHA-256:
~/.cue-cli/sessions/sid_f5a682549ea6/recording.wav—5745fdc5272427b8fae907fea11cf0e2e947ccd66bd364a82e6ea71fe1bc2215.~/.cue-cli/sessions/sid_f5a682549ea6/recordings/001_run/recording.wav—8e52359e73dd2eb64fb942a85c544bc662f0c04601682ad45639f03c6b31bbd0.~/.cue-cli/sessions/sid_f5a682549ea6/recordings/002_run/recording.wav—9bdc45a68f897955a1ffe7d6513e7877babd6c6f8aa409fb62702d1b44a14721.~/.cue-cli/sessions/sid_f5a682549ea6/recordings/001_run/result.json—5be009b38e7c6c91e2f1f02b8f11f6e943bdf2176536065b647af56144bbdb56.~/.cue-cli/sessions/sid_f5a682549ea6/recordings/002_run/result.json—3f98d27ae2985eb88394204fc7881481f97badbc36f7521bdaa9f30f5149de43./tmp/task-2017-final-worker.log—837b04c57b7a845bcb487cde918fc19de2fb8ae8f374cf846753f7aafd1e5ca0.Boundary limitation: proxy timestamps are protocol-compatible provider receive/forward timestamps, not internal Fish server timestamps. SDK immediacy is established by the deterministic first-event test and corroborated by Cue TTFB and speech timing.
Review
Independent reconciliation reviews returned:
Spec compliance: APPROVED.Code quality: APPROVEDwith no Critical/Important findings.Ported from livekit/agents#6368 and reconciled with livekit/agents#6423 and #2033.